home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
nethack.lha
/
nethack-3.1
/
src
/
weapon.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-16
|
14KB
|
536 lines
/* SCCS Id: @(#)weapon.c 3.1 93/01/15 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
/*
* This module contains code for calculation of "to hit" and damage
* bonuses for any given weapon used, as well as weapons selection
* code for monsters.
*/
#include "hack.h"
#ifdef OVLB
static const char NEARDATA kebabable[] = { S_XORN, S_DRAGON, S_NAGA, S_GIANT, 0 };
/*
* hitval returns an integer representing the "to hit" bonuses
* of "otmp" against the monster type "ptr".
*/
int
hitval(otmp, ptr)
struct obj *otmp;
struct permonst *ptr;
{
int tmp = 0;
if (otmp->oclass == WEAPON_CLASS ||
otmp->otyp == PICK_AXE || otmp->otyp == UNICORN_HORN)
tmp += otmp->spe;
/* Put weapon specific "to hit" bonuses in below: */
tmp += objects[otmp->otyp].oc_hitbon;
/* Put weapon vs. monster type "to hit" bonuses in below: */
/* Blessed weapons used against undead or demons */
if(otmp->oclass == WEAPON_CLASS && otmp->blessed &&
(is_demon(ptr) || is_undead(ptr))) tmp += 2;
if(otmp->otyp >= SPEAR && otmp->otyp <= JAVELIN &&
index(kebabable, ptr->mlet)) tmp += 2;
/* Check specially named weapon "to hit" bonuses */
if (otmp->oartifact) tmp += spec_abon(otmp, ptr);
return tmp;
}
/*
* dmgval returns an integer representing the damage bonuses
* of "otmp" against the monster type "ptr".
*/
int
dmgval(otmp, ptr)
struct obj *otmp;
struct permonst *ptr;
{
int tmp = 0;
if(otmp->otyp == CREAM_PIE) return(0);
if(ptr->msize >= MZ_HUMAN) {
if(objects[otmp->otyp].oc_wldam)
tmp = rnd(objects[otmp->otyp].oc_wldam);
switch (otmp->otyp) {
case CROSSBOW_BOLT:
case MORNING_STAR:
case PARTISAN:
case ELVEN_BROADSWORD:
case BROADSWORD: tmp++; break;
case FLAIL:
case RANSEUR:
case VOULGE: tmp += rnd(4); break;
case ACID_VENOM:
case HALBERD:
case SPETUM: tmp += rnd(6); break;
case BATTLE_AXE:
case BARDICHE:
case TRIDENT: tmp += d(2,4); break;
case TSURUGI:
case DWARVISH_MATTOCK:
case TWO_HANDED_SWORD: tmp += d(2,6); break;
}
} else {
if(objects[otmp->otyp].oc_wsdam)
tmp = rnd(objects[otmp->otyp].oc_wsdam);
switch (otmp->otyp) {
case CROSSBOW_BOLT:
case MACE:
case WAR_HAMMER:
case FLAIL:
case SPETUM:
case TRIDENT: tmp++; break;
case BATTLE_AXE:
case BARDICHE:
case BILL_GUISARME:
case GUISARME:
case LUCERN_HAMMER:
case MORNING_STAR:
case RANSEUR:
case BROADSWORD:
case ELVEN_BROADSWORD:
case VOULGE: tmp += rnd(4); break;
case ACID_VENOM: tmp += rnd(6); break;
}
}
if (otmp->oclass == WEAPON_CLASS || otmp->otyp == PICK_AXE
|| otmp->otyp == UNICORN_HORN)
tmp += otmp->spe;
tmp -= otmp->oeroded;
if (objects[otmp->otyp].oc_material <= LEATHER && thick_skinned(ptr))
/* thick skinned/scaled creatures don't feel it */
tmp = 0;
if (ptr == &mons[PM_SHADE] && objects[otmp->otyp].oc_material != SILVER)
tmp = 0;
/* Put weapon vs. monster type damage bonuses in below: */
if(otmp->oclass == WEAPON_CLASS) {
if (otmp->blessed && (is_undead(ptr) || is_demon(ptr)))
tmp += rnd(4);
if ((otmp->otyp == AXE || otmp->otyp == BATTLE_AXE)
&& is_wooden(ptr))
tmp += rnd(4);
if (objects[otmp->otyp].oc_material == SILVER && hates_silver(ptr))
tmp += rnd(20);
}
return(tmp);
}
void
set_uasmon() /* update the "uasmon" structure */
{
#ifdef POLYSELF
if(u.umonnum >= 0) uasmon = &mons[u.umonnum];
else {
#endif
uasmon = &playermon;
playermon.mlevel = u.ulevel;
playermon.ac = u.uac;
playermon.mr = (u.ulevel > 8) ? 5 * (u.ulevel-7) : u.ulevel;
#ifdef POLYSELF
}
#endif
return;
}
#endif /* OVLB */
#ifdef OVL0
#define Oselect(x) if((otmp = oselect(mtmp, x))) return(otmp);
static struct obj * FDECL(oselect, (struct monst *,int));
static struct obj *
oselect(mtmp, x)
struct monst *mtmp;
int x;
{
struct obj *otmp;
for(otmp=mtmp->minvent; otmp; otmp = otmp->nobj) {
if (otmp->otyp == x && touch_artifact(otmp,mtmp))
return otmp;
}
return (struct obj *)0;
}
static const int NEARDATA rwep[] =
{ DWARVISH_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, JAVELIN,
SHURIKEN, SILVER_ARROW, ELVEN_ARROW, ARROW, ORCISH_ARROW,
CROSSBOW_BOLT, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, KNIFE, ROCK,
LOADSTONE, LUCKSTONE, DART, /* BOOMERANG, */ CREAM_PIE
/* note: CREAM_PIE should NOT be #ifdef KOPS */
};
static struct obj *propellor;
struct obj *
select_rwep(mtmp) /* select a ranged weapon for the monster */
register struct monst *mtmp;
{
register struct obj *otmp;
int i;
#ifdef KOPS
char mlet = mtmp->data->mlet;
#endif
propellor = &zeroobj;
#ifdef KOPS
if(mlet == S_KOP) /* pies are first choice for Kops */
Oselect(CREAM_PIE);
#endif
if(throws_rocks(mtmp->data)) /* ...boulders for giants */
Oselect(BOULDER);
/*
* other than these two specific cases, always select the
* most potent ranged weapon to hand.
*/
for (i = 0; i < SIZE(rwep); i++) {
int prop;
propellor = &zeroobj;
/* shooting gems from slings; this goes just before the darts */
if (rwep[i]==DART && !likes_gems(mtmp->data)
&& (propellor = m_carrying(mtmp, SLING))) {
for(otmp=mtmp->minvent; otmp; otmp=otmp->nobj) {
if(otmp->oclass==GEM_CLASS &&
(otmp->otyp != LOADSTONE || !otmp->cursed))
return(otmp);
}
}
prop = (objects[rwep[i]]).w_propellor;
if (prop > 0) {
switch (prop) {
case WP_BOW:
propellor = (oselect(mtmp, ELVEN_BOW));
if (!propellor) propellor = (oselect(mtmp, BOW));
if (!propellor) propellor = (oselect(mtmp, ORCISH_BOW));
break;
case WP_SLING:
propellor = (oselect(mtmp, SLING));
break;
case WP_CROSSBOW:
propellor = (oselect(mtmp, CROSSBOW));
}
#ifdef MUSE
if ((otmp = MON_WEP(mtmp)) && otmp->cursed && otmp != propellor
&& mtmp->weapon_check == NO_WEAPON_WANTED)
propellor = 0;
#endif
}
/* propellor = obj, propellor to use
* propellor = &zeroobj, doesn't need a propellor
* propellor = 0, needed one and didn't have one
*/
if (propellor != 0) {
/* Note: cannot use m_carrying for loadstones, since it will
* always select the first object of a type, and maybe the
* monster is carrying two but only the first is unthrowable.
*/
if (rwep[i] != LOADSTONE) {
#ifdef MUSE
/* Don't throw a cursed weapon-in-hand */
if ((otmp = oselect(mtmp, rwep[i]))
&& (!otmp->cursed || otmp != MON_WEP(mtmp)))
return(otmp);
#else
Oselect(rwep[i]);
#endif
} else for(otmp=mtmp->minvent; otmp; otmp=otmp->nobj) {
if (otmp->otyp == LOADSTONE && !otmp->cursed)
return otmp;
}
}
}
/* failure */
return (struct obj *)0;
}
/* 0 = used by any monster; 1 = only used by strong monsters */
static const struct hwep { short otyp, big; } NEARDATA hwep[] = {
{TSURUGI,1}, {RUNESWORD,0},
{DWARVISH_MATTOCK,1}, {TWO_HANDED_SWORD,1}, {BATTLE_AXE,1},
{KATANA,0}, {UNICORN_HORN,1}, {CRYSKNIFE,0},
{TRIDENT,0}, {LONG_SWORD,0}, {ELVEN_BROADSWORD,0}, {BROADSWORD,0},
{LUCERN_HAMMER,1}, {SCIMITAR,1}, {SILVER_SABER,0}, {HALBERD,1},
{PARTISAN,1}, {LANCE,1}, {FAUCHARD,1}, {BILL_GUISARME,1},
{BEC_DE_CORBIN,1}, {GUISARME,1}, {RANSEUR,1}, {SPETUM,1},
{VOULGE,1}, {BARDICHE,0}, {MORNING_STAR,0}, {GLAIVE,0},
{ELVEN_SHORT_SWORD,0}, {DWARVISH_SHORT_SWORD,0}, {SHORT_SWORD,0},
{ORCISH_SHORT_SWORD,0}, {MACE,0}, {AXE,0}, {DWARVISH_SPEAR,0},
{ELVEN_SPEAR,0}, {SPEAR,0}, {ORCISH_SPEAR,0}, {FLAIL,0},
{QUARTERSTAFF,1}, {JAVELIN,0}, {AKLYS,0}, {CLUB,0}, {PICK_AXE,0},
#ifdef KOPS
{RUBBER_HOSE,0},
#endif /* KOPS */
{WAR_HAMMER,0}, {ELVEN_DAGGER,0}, {DAGGER,0}, {ORCISH_DAGGER,0},
{ATHAME,0}, {SCALPEL,0}, {KNIFE,0}, {WORM_TOOTH,0}, {BULLWHIP,0}
};
struct obj *
select_hwep(mtmp) /* select a hand to hand weapon for the monster */
register struct monst *mtmp;
{
register struct obj *otmp;
register int i;
register const struct hwep *hw;
boolean strong = strongmonst(mtmp->data);
if(is_giant(mtmp->data)) /* giants just love to use clubs */
Oselect(CLUB);
/* only strong monsters can wield big (esp. long) weapons */
/* all monsters can wield the remaining weapons */
for (i = 0, hw = hwep; i < SIZE(hwep); i++, hw++)
if ((strong || !hw->big) &&
#ifdef MUSE
(!objects[hw->otyp].oc_bimanual || !which_armor(mtmp, W_ARMS)) &&
#endif
(objects[hw->otyp].oc_material != SILVER || !hates_silver(mtmp->data)))
Oselect(hw->otyp);
/* failure */
return (struct obj *)0;
}
#ifdef MUSE
/* Called after polymorphing a monster, robbing it, etc.... Monsters
* otherwise never unwield stuff on their own. Shouldn't print messages.
*/
void
possibly_unwield(mon)
register struct monst *mon;
{
register struct obj *obj;
struct obj *otmp, *backobj, *mw_tmp;
if (!(mw_tmp = MON_WEP(mon)))
return;
for(obj=mon->minvent; obj; obj=obj->nobj)
if (obj == mw_tmp) break;
if (!obj) { /* The weapon was stolen or destroyed */
MON_NOWEP(mon);
mon->weapon_check = NEED_WEAPON;
return;
}
if (!attacktype(mon->data, AT_WEAP)) {
MON_NOWEP(mon);
mon->weapon_check = NO_WEAPON_WANTED;
if (cansee(mon->mx, mon->my)) {
pline("%s drops %s.", Monnam(mon),
distant_name(obj, doname));
}
backobj = 0;
for(otmp = mon->minvent; otmp; otmp = otmp->nobj) {
/* flooreffects unnecessary, can't wield boulders */
if (otmp == obj) {
if (!backobj) mon->minvent = otmp->nobj;
else backobj->nobj = otmp->nobj;
place_object(otmp, mon->mx, mon->my);
otmp->nobj = fobj;
fobj = otmp;
stackobj(fobj);
if(cansee(mon->mx,mon->my))
newsym(mon->mx, mon->my);
break;
}
backobj = otmp;
}
return;
}
/* The remaining case where there is a change is where a monster
* is polymorphed into a stronger/weaker monster with a different
* choice of weapons. This has no parallel for players. It can
* be handled by waiting until mon_wield_item is actually called.
* Though the monster still wields the wrong weapon until then,
* this is OK since the player can't see it.
* Possible problem: big monster with big cursed weapon gets
* polymorphed into little monster. But it's not quite clear how to
* handle this anyway....
*/
mon->weapon_check = NEED_WEAPON;
return;
}
/* Let a monster try to wield a weapon, based on mon->weapon_check.
* Returns 1 if the monster took time to do it, 0 if it did not.
*/
int
mon_wield_item(mon)
register struct monst *mon;
{
struct obj *obj;
/* This case actually should never happen */
if (mon->weapon_check == NO_WEAPON_WANTED) return 0;
switch(mon->weapon_check) {
case NEED_HTH_WEAPON:
obj = select_hwep(mon);
break;
case NEED_RANGED_WEAPON:
(void)select_rwep(mon);
obj = propellor;
break;
case NEED_PICK_AXE:
obj = m_carrying(mon, PICK_AXE);
break;
default: impossible("weapon_check %d for %s?",
mon->weapon_check, mon_nam(mon));
return 0;
}
if (obj && obj != &zeroobj) {
struct obj *mw_tmp = MON_WEP(mon);
if (mw_tmp == obj) { /* already wielding it */
mon->weapon_check = NEED_WEAPON;
return 0;
}
/* Actually, this isn't necessary--as soon as the monster
* wields the weapon, the weapon welds itself, so the monster
* can know it's cursed and needn't even bother trying.
* Still....
*/
if (mw_tmp && mw_tmp->cursed) {
if (obj->otyp == PICK_AXE) {
if (canseemon(mon)) {
pline("Since %s weapon %s welded to %s hand,",
s_suffix(mon_nam(mon)),
(mw_tmp->quan == 1L) ? "is" : "are",
humanoid(mon->data)
? (mon->female ? "her" : "his")
: "its");
pline("%s cannot wield that %s.",
mon_nam(mon), xname(obj));
mw_tmp->bknown = 1;
}
} else {
if (canseemon(mon)) {
pline("%s tries to wield %s.", Monnam(mon),
doname(obj));
pline("%s %s %s welded to %s hand!",
s_suffix(Monnam(mon)), xname(mw_tmp),
(mw_tmp->quan == 1L) ? "is" : "are",
humanoid(mon->data)
? (mon->female ? "her" : "his")
: "its");
mw_tmp->bknown = 1;
}
}
mon->weapon_check = NO_WEAPON_WANTED;
return 1;
}
mon->mw = obj; /* wield obj */
mon->weapon_check = NEED_WEAPON;
if (canseemon(mon)) {
pline("%s wields %s!", Monnam(mon), doname(obj));
if (obj->cursed) {
pline("%s %s to %s hand!",
The(xname(obj)),
(obj->quan == 1L) ? "welds itself"
: "weld themselves",
s_suffix(mon_nam(mon)));
obj->bknown = 1;
}
}
return 1;
}
mon->weapon_check = NEED_WEAPON;
return 0;
}
/* rearrange a monster's inventory so that wielded weapon is first */
void
sort_mwep(mon)
struct monst *mon;
{
struct obj *otmp, *prev, *mw_tmp = MON_WEP(mon);
if (!mw_tmp) return;
for (otmp = mon->minvent, prev = 0; otmp; otmp = otmp->nobj) {
if (otmp == mw_tmp) break;
prev = otmp;
}
if (!otmp) {
MON_NOWEP(mon);
} else if (prev) {
prev->nobj = otmp->nobj;
otmp->nobj = mon->minvent;
mon->minvent = otmp;
}
}
#endif
int
abon() { /* attack bonus for strength & dexterity */
int sbon;
register int str = ACURR(A_STR), dex = ACURR(A_DEX);
#ifdef POLYSELF
if (u.umonnum >= 0) return(adj_lev(&mons[u.umonnum])-3);
#endif
if (str < 6) sbon = -2;
else if (str < 8) sbon = -1;
else if (str < 17) sbon = 0;
else if (str < 69) sbon = 1; /* up to 18/50 */
else if (str < 118) sbon = 2;
else sbon = 3;
/*
* Temporary kludge - make it a bit easier for a low level character
* to hit until we tune the game a little better.
*/
sbon += (u.ulevel < 3) ? 1 : 0;
if (dex < 4) return(sbon-3);
else if (dex < 6) return(sbon-2);
else if (dex < 8) return(sbon-1);
else if (dex < 14) return(sbon);
else return(sbon + dex-14);
}
#endif /* OVL0 */
#ifdef OVL1
int
dbon() { /* damage bonus for strength */
register int str = ACURR(A_STR);
#ifdef POLYSELF
if (u.umonnum >= 0) return(0);
#endif
if (str < 6) return(-1);
else if (str < 16) return(0);
else if (str < 18) return(1);
else if (str == 18) return(2); /* up to 18 */
else if (str < 94) return(3); /* up to 18/75 */
else if (str < 109) return(4); /* up to 18/90 */
else if (str < 118) return(5); /* up to 18/99 */
else return(6);
}
#endif /* OVL1 */
/*weapon.c*/